﻿using System;
using l1cs.server.datatables;
using l1cs.server.model.Instance;
using l1cs.server.serverpackets;
using l1cs.server.templates;

namespace l1cs.server.model
{
    public class L1PcInventory : L1Inventory
    {
        private static Logger _log = Logger.getLogger( typeof( L1PcInventory ).FullName );

        private const int MAX_SIZE = 180;

        private L1PcInstance _owner; // 所有者プレイヤー

        private int _arrowId; // 優先して使用されるアローのItemID

        private int _stingId; // 優先して使用されるスティングのItemID

        public L1PcInventory( L1PcInstance owner )
        {
            _owner = owner;
            _arrowId = 0;
            _stingId = 0;
        }

        public L1PcInstance getOwner()
        {
            return _owner;
        }

        /// <summary>
        /// 240段階のウェイトを返す
        /// </summary>
        /// <returns></returns>
        public int getWeight240()
        {
            return calcWeight240( getWeight() );
        }

        /// <summary>
        /// 240段階のウェイトを算出する
        /// </summary>
        /// <param name="weight"></param>
        /// <returns></returns>
        public int calcWeight240( int weight )
        {
            int weight240 = 0;
            if ( Config.RATE_WEIGHT_LIMIT != 0 )
            {
                double maxWeight = _owner.getMaxWeight();
                if ( weight > maxWeight )
                {
                    weight240 = 240;
                }
                else
                {
                    double wpTemp = ( weight * 100 / maxWeight ) * 240.00 / 100.00;
                    //不要なんじゃない？
                    //DecimalFormat df = new DecimalFormat("00.##");
                    //df.format(wpTemp);
                    wpTemp = Math.Round( wpTemp );
                    weight240 = (int)( wpTemp );
                }
            }
            else
            {
                // ウェイトレートが０なら重量常に０
                weight240 = 0;
            }
            return weight240;
        }

        override public int checkAddItem( L1ItemInstance item, int count )
        {
            return checkAddItem( item, count, true );
        }

        public int checkAddItem( L1ItemInstance item, int count, bool message )
        {
            if ( item == null )
            {
                return -1;
            }
            if ( getSize() > MAX_SIZE
                    || ( getSize() == MAX_SIZE &&
                       ( !item.isStackable() || !checkItem( item.getItem().getItemId() ) ) ) )
            {
                // 容量確認
                if ( message )
                {
                    sendOverMessage( 263 ); // \f1一人のキャラクターが持って歩けるアイテムは最大180個までです。
                }
                return SIZE_OVER;
            }

            int weight = getWeight() + item.getItem().getWeight() * count / 1000 + 1;
            if ( weight < 0 || ( item.getItem().getWeight() * count / 1000 ) < 0 )
            {
                if ( message )
                {
                    sendOverMessage( 82 ); // アイテムが重すぎて、これ以上持てません。
                }
                return WEIGHT_OVER;
            }
            if ( calcWeight240( weight ) >= 240 )
            {
                if ( message )
                {
                    sendOverMessage( 82 ); // アイテムが重すぎて、これ以上持てません。
                }
                return WEIGHT_OVER;
            }

            L1ItemInstance itemExist = findItemId( item.getItemId() );
            if ( itemExist != null && ( itemExist.getCount() + count ) > MAX_AMOUNT )
            {
                if ( message )
                {
                    getOwner().sendPackets( new S_ServerMessage( 166,
                            "所持しているアデナ",
                            "2,000,000,000を超過しています。" ) ); // \f1%0が%4%1%3%2
                }
                return AMOUNT_OVER;
            }

            return OK;
        }

        public void sendOverMessage( int message_id )
        {
            _owner.sendPackets( new S_ServerMessage( message_id ) );
        }

        /// <summary>
        /// ＤＢのcharacter_itemsから持ち物を取得する。
        /// </summary>
        override public void loadItems()
        {
            foreach ( L1ItemInstance item in CharacterItemsTable.loadItems( _owner.getId() ) )
            {
                _items.Add( item );

                if ( item.isEquipped() )
                {
                    item.setEquipped( false );
                    setEquipped( item, true, true, false );
                }
                if ( item.getItem().getType2() == 0
                  && item.getItem().getType() == 2 )
                {
                    // light系アイテム
                    item.setRemainingTime( item.getItem().getLightFuel() );
                }
                L1World.getInstance().storeObject( item );
            }
        }

        /// <summary>
        /// ＤＢのcharacter_itemsへ登録
        /// </summary>
        /// <param name="item"></param>
        override
        public void insertItem( L1ItemInstance item )
        {
            _owner.sendPackets( new S_AddItem( item ) );
            if ( item.getItem().getWeight() != 0 )
            {
                _owner.sendPackets( new S_PacketBox( S_PacketBox.WEIGHT, getWeight240() ) );
            }
            CharacterItemsTable.storeItem( _owner.getId(), item );
        }

        public const int COL_ATTR_ENCHANT_LEVEL = 2048;
        public const int COL_ATTR_ENCHANT_KIND = 1024;
        public const int COL_BLESS = 512;
        public const int COL_REMAINING_TIME = 256;
        public const int COL_CHARGE_COUNT = 128;
        public const int COL_ITEMID = 64;
        public const int COL_DELAY_EFFECT = 32;
        public const int COL_COUNT = 16;
        public const int COL_EQUIPPED = 8;
        public const int COL_ENCHANTLVL = 4;
        public const int COL_IS_ID = 2;
        public const int COL_DURABILITY = 1;

        override
        public void updateItem( L1ItemInstance item )
        {
            updateItem( item, COL_COUNT );
            if ( item.getItem().isToBeSavedAtOnce() )
            {
                saveItem( item, COL_COUNT );
            }
        }

        /// <summary>
        /// インベントリ内のアイテムの状態を更新する。
        /// </summary>
        /// <param name="item">更新対象のアイテム</param>
        /// <param name="column">更新するステータスの種類</param>
        override public void updateItem( L1ItemInstance item, int column )
        {
            if ( column >= COL_ATTR_ENCHANT_LEVEL ) // 属性強化数
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                column -= COL_ATTR_ENCHANT_LEVEL;
            }
            if ( column >= COL_ATTR_ENCHANT_KIND ) // 属性強化の種類
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                column -= COL_ATTR_ENCHANT_KIND;
            }
            if ( column >= COL_BLESS ) // 祝福・封印
            {
                _owner.sendPackets( new S_ItemColor( item ) );
                column -= COL_BLESS;
            }
            if ( column >= COL_REMAINING_TIME ) // 使用可能な残り時間
            {
                _owner.sendPackets( new S_ItemName( item ) );
                column -= COL_REMAINING_TIME;
            }
            if ( column >= COL_CHARGE_COUNT ) // チャージ数
            {
                _owner.sendPackets( new S_ItemName( item ) );
                column -= COL_CHARGE_COUNT;
            }
            if ( column >= COL_ITEMID )// 別のアイテムになる場合(便箋を開封したときなど)
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                _owner.sendPackets( new S_ItemColor( item ) );
                _owner.sendPackets( new S_PacketBox( S_PacketBox.WEIGHT, getWeight240() ) );
                column -= COL_ITEMID;
            }
            if ( column >= COL_DELAY_EFFECT ) // 効果ディレイ
            {
                column -= COL_DELAY_EFFECT;
            }
            if ( column >= COL_COUNT ) // カウント
            {
                _owner.sendPackets( new S_ItemAmount( item ) );

                int weight = item.getWeight();
                if ( weight != item.getLastWeight() )
                {
                    item.setLastWeight( weight );
                    _owner.sendPackets( new S_ItemStatus( item ) );
                }
                else
                {
                    _owner.sendPackets( new S_ItemName( item ) );
                }
                if ( item.getItem().getWeight() != 0 )
                {
                    // XXX 240段階のウェイトが変化しない場合は送らなくてよい
                    _owner.sendPackets( new S_PacketBox( S_PacketBox.WEIGHT, getWeight240() ) );
                }
                column -= COL_COUNT;
            }
            if ( column >= COL_EQUIPPED ) // 装備状態
            {
                _owner.sendPackets( new S_ItemName( item ) );
                column -= COL_EQUIPPED;
            }
            if ( column >= COL_ENCHANTLVL ) // エンチャント
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                column -= COL_ENCHANTLVL;
            }
            if ( column >= COL_IS_ID ) // 確認状態
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                _owner.sendPackets( new S_ItemColor( item ) );
                column -= COL_IS_ID;
            }
            if ( column >= COL_DURABILITY ) // 耐久性
            {
                _owner.sendPackets( new S_ItemStatus( item ) );
                column -= COL_DURABILITY;
            }
        }

        /// <summary>
        /// インベントリ内のアイテムの状態をDBに保存する。
        /// </summary>
        /// <param name="item">更新対象のアイテム</param>
        /// <param name="column">更新するステータスの種類</param>
        public void saveItem( L1ItemInstance item, int column )
        {
            if ( column == 0 )
            {
                return;
            }
            if ( column >= COL_ATTR_ENCHANT_LEVEL ) // 属性強化数
            {
                CharacterItemsTable.updateItemAttrEnchantLevel( item );
                column -= COL_ATTR_ENCHANT_LEVEL;
            }
            if ( column >= COL_ATTR_ENCHANT_KIND ) // 属性強化の種類
            {
                CharacterItemsTable.updateItemAttrEnchantKind( item );
                column -= COL_ATTR_ENCHANT_KIND;
            }
            if ( column >= COL_BLESS ) // 祝福・封印
            {
                CharacterItemsTable.updateItemBless( item );
                column -= COL_BLESS;
            }
            if ( column >= COL_REMAINING_TIME ) // 使用可能な残り時間
            {
                CharacterItemsTable.updateItemRemainingTime( item );
                column -= COL_REMAINING_TIME;
            }
            if ( column >= COL_CHARGE_COUNT ) // チャージ数
            {
                CharacterItemsTable.updateItemChargeCount( item );
                column -= COL_CHARGE_COUNT;
            }
            if ( column >= COL_ITEMID ) // 別のアイテムになる場合(便箋を開封したときなど)
            {
                CharacterItemsTable.updateItemId( item );
                column -= COL_ITEMID;
            }
            if ( column >= COL_DELAY_EFFECT ) // 効果ディレイ
            {
                CharacterItemsTable.updateItemDelayEffect( item );
                column -= COL_DELAY_EFFECT;
            }
            if ( column >= COL_COUNT ) // カウント
            {
                CharacterItemsTable.updateItemCount( item );
                column -= COL_COUNT;
            }
            if ( column >= COL_EQUIPPED ) // 装備状態
            {
                CharacterItemsTable.updateItemEquipped( item );
                column -= COL_EQUIPPED;
            }
            if ( column >= COL_ENCHANTLVL ) // エンチャント
            {
                CharacterItemsTable.updateItemEnchantLevel( item );
                column -= COL_ENCHANTLVL;
            }
            if ( column >= COL_IS_ID ) // 確認状態
            {
                CharacterItemsTable.updateItemIdentified( item );
                column -= COL_IS_ID;
            }
            if ( column >= COL_DURABILITY ) // 耐久性
            {
                CharacterItemsTable.updateItemDurability( item );
                column -= COL_DURABILITY;
            }
        }

        /// <summary>
        /// ＤＢのcharacter_itemsから削除
        /// </summary>
        /// <param name="item"></param>
        override public void deleteItem( L1ItemInstance item )
        {
            CharacterItemsTable.deleteItem( item );

            if ( item.isEquipped() )
            {
                setEquipped( item, false );
            }
            _owner.sendPackets( new S_DeleteInventoryItem( item ) );
            lock ( _items )
            {
                _items.Remove( item );
            }
            if ( item.getItem().getWeight() != 0 )
            {
                _owner.sendPackets( new S_PacketBox( S_PacketBox.WEIGHT, getWeight240() ) );
            }
        }

        /// <summary>
        /// アイテムを装着脱着させる（L1ItemInstanceの変更、補正値の設定、character_itemsの更新、パケット送信まで管理）
        /// </summary>
        /// <param name="item"></param>
        /// <param name="equipped"></param>
        public void setEquipped( L1ItemInstance item, bool equipped )
        {
            setEquipped( item, equipped, false, false );
        }

        public void setEquipped( L1ItemInstance item, bool equipped,
                bool loaded, bool changeWeapon )
        {
            if ( item.isEquipped() != equipped )
            { // 設定値と違う場合だけ処理
                L1Item temp = item.getItem();
                if ( equipped )
                { // 装着
                    item.setEquipped( true );
                    _owner.getEquipSlot().set( item );
                }
                else
                { // 脱着
                    if ( !loaded )
                    {
                        // インビジビリティクローク バルログブラッディクローク装備中でインビジ状態の場合はインビジ状態の解除
                        if ( temp.getItemId() == 20077
                           || temp.getItemId() == 20062
                           || temp.getItemId() == 120077 )
                        {
                            if ( _owner.isInvisble() )
                            {
                                _owner.delInvis();
                                return;
                            }
                        }
                    }
                    item.setEquipped( false );
                    _owner.getEquipSlot().remove( item );
                }
                if ( !loaded )
                { // 最初の読込時はＤＢパケット関連の処理はしない
                    // XXX:意味のないセッター
                    _owner.setCurrentHp( _owner.getCurrentHp() );
                    _owner.setCurrentMp( _owner.getCurrentMp() );
                    updateItem( item, COL_EQUIPPED );
                    _owner.sendPackets( new S_OwnCharStatus( _owner ) );
                    if ( temp.getType2() == 1 && changeWeapon == false )
                    { // 武器の場合はビジュアル更新。ただし、武器の持ち替えで武器を脱着する時は更新しない
                        _owner.sendPackets( new S_CharVisualUpdate( _owner ) );
                        _owner.broadcastPacket( new S_CharVisualUpdate( _owner ) );
                    }
                    // _owner.getNetConnection().saveCharToDisk(_owner); //
                    // DBにキャラクター情報を書き込む
                }
            }
        }

        /// <summary>
        /// 特定のアイテムを装備しているか確認
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool checkEquipped( int id )
        {
            lock ( _items )
            {
                foreach ( Object itemObject in _items )
                {
                    L1ItemInstance item = (L1ItemInstance)itemObject;
                    if ( item.getItem().getItemId() == id && item.isEquipped() )
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        ///  特定のアイテムを全て装備しているか確認（セットボーナスがあるやつの確認用）
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public bool checkEquipped( int[] ids )
        {
            foreach ( int id in ids )
            {
                if ( !checkEquipped( id ) )
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 特定のタイプのアイテムを装備している数
        /// </summary>
        /// <param name="type2"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public int getTypeEquipped( int type2, int type )
        {
            int equipeCount = 0;
            lock ( _items )
            {
                foreach ( Object itemObject in _items )
                {
                    L1ItemInstance item = (L1ItemInstance)itemObject;
                    if ( item.getItem().getType2() == type2
                      && item.getItem().getType() == type && item.isEquipped() )
                    {
                        equipeCount++;
                    }
                }
            }
            return equipeCount;
        }

        /// <summary>
        /// 装備している特定のタイプのアイテム
        /// </summary>
        /// <param name="type2"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public L1ItemInstance getItemEquipped( int type2, int type )
        {
            L1ItemInstance equipeitem = null;
            lock ( _items )
            {
                foreach ( Object itemObject in _items )
                {
                    L1ItemInstance item = (L1ItemInstance)itemObject;
                    if ( item.getItem().getType2() == type2
                      && item.getItem().getType() == type
                      && item.isEquipped() )
                    {
                        equipeitem = item;
                        break;
                    }
                }
            }
            return equipeitem;
        }

        /// <summary>
        /// 装備しているリング
        /// </summary>
        /// <returns></returns>
        public L1ItemInstance[] getRingEquipped()
        {
            L1ItemInstance[] equipeItem = new L1ItemInstance[2];
            int equipeCount = 0;
            lock ( _items )
            {
                foreach ( Object itemObject in _items )
                {
                    L1ItemInstance item = (L1ItemInstance)itemObject;
                    if ( item.getItem().getType2() == 2
                      && item.getItem().getType() == 9
                      && item.isEquipped() )
                    {
                        equipeItem[equipeCount] = item;
                        equipeCount++;
                        if ( equipeCount == 2 )
                        {
                            break;
                        }
                    }
                }
            }
            return equipeItem;
        }

        /// <summary>
        /// 変身時に装備できない装備を外す
        /// </summary>
        /// <param name="polyid"></param>
        public void takeoffEquip( int polyid )
        {
            takeoffWeapon( polyid );
            takeoffArmor( polyid );
        }

        /// <summary>
        /// 変身時に装備できない武器を外す
        /// </summary>
        /// <param name="polyid"></param>
        private void takeoffWeapon( int polyid )
        {
            if ( _owner.getWeapon() == null )
            { // 素手
                return;
            }

            bool takeoff = false;
            int weapon_type = _owner.getWeapon().getItem().getType();
            // 装備出来ない武器を装備してるか？
            takeoff = !L1PolyMorph.isEquipableWeapon( polyid, weapon_type );

            if ( takeoff )
            {
                setEquipped( _owner.getWeapon(), false, false, false );
            }
        }

        /// <summary>
        /// 変身時に装備できない防具を外す
        /// </summary>
        /// <param name="polyid"></param>
        private void takeoffArmor( int polyid )
        {
            L1ItemInstance armor = null;

            // ヘルムからガーダーまでチェックする
            for ( int type = 0; type <= 13; type++ )
            {
                // 装備していて、装備不可の場合は外す
                if ( getTypeEquipped( 2, type ) != 0
                 && !L1PolyMorph.isEquipableArmor( polyid, type ) )
                {
                    if ( type == 9 )
                    { // リングの場合は、両手分外す
                        armor = getItemEquipped( 2, type );
                        if ( armor != null )
                        {
                            setEquipped( armor, false, false, false );
                        }
                        armor = getItemEquipped( 2, type );
                        if ( armor != null )
                        {
                            setEquipped( armor, false, false, false );
                        }
                    }
                    else
                    {
                        armor = getItemEquipped( 2, type );
                        if ( armor != null )
                        {
                            setEquipped( armor, false, false, false );
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 使用するアローの取得
        /// </summary>
        /// <returns></returns>
        public L1ItemInstance getArrow()
        {
            return getBullet( 0 );
        }

        /// <summary>
        /// 使用するスティングの取得
        /// </summary>
        /// <returns></returns>
        public L1ItemInstance getSting()
        {
            return getBullet( 15 );
        }

        private L1ItemInstance getBullet( int type )
        {
            L1ItemInstance bullet;
            int priorityId = 0;
            if ( type == 0 )
            {
                priorityId = _arrowId; // アロー
            }
            if ( type == 15 )
            {
                priorityId = _stingId; // スティング
            }
            if ( priorityId > 0 ) // 優先する弾があるか
            {
                bullet = findItemId( priorityId );
                if ( bullet != null )
                {
                    return bullet;
                }
                else // なくなっていた場合は優先を消す
                {
                    if ( type == 0 )
                    {
                        _arrowId = 0;
                    }
                    if ( type == 15 )
                    {
                        _stingId = 0;
                    }
                }
            }
            lock ( _items )
            {
                foreach ( Object itemObject in _items ) // 弾を探す
                {
                    bullet = (L1ItemInstance)itemObject;
                    if ( bullet.getItem().getType() == type )
                    {
                        if ( type == 0 )
                        {
                            _arrowId = bullet.getItem().getItemId(); // 優先にしておく
                        }
                        if ( type == 15 )
                        {
                            _stingId = bullet.getItem().getItemId(); // 優先にしておく
                        }
                        return bullet;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 優先するアローの設定
        /// </summary>
        /// <param name="id"></param>
        public void setArrow( int id )
        {
            _arrowId = id;
        }

        /// <summary>
        /// 優先するスティングの設定
        /// </summary>
        /// <param name="id"></param>
        public void setSting( int id )
        {
            _stingId = id;
        }

        /// <summary>
        /// 装備によるＨＰ自然回復補正
        /// </summary>
        /// <returns></returns>
        public int hpRegenPerTick()
        {
            int hpr = 0;
            foreach ( Object itemObject in _items )
            {
                L1ItemInstance item = (L1ItemInstance)itemObject;
                if ( item.isEquipped() )
                {
                    hpr += item.getItem().get_addhpr();
                }
            }
            return hpr;
        }

        /// <summary>
        /// 装備によるＭＰ自然回復補正
        /// </summary>
        /// <returns></returns>
        public int mpRegenPerTick()
        {
            int mpr = 0;
            foreach ( Object itemObject in _items )
            {
                L1ItemInstance item = (L1ItemInstance)itemObject;
                if ( item.isEquipped() )
                {
                    mpr += item.getItem().get_addmpr();
                }
            }
            return mpr;
        }

        public L1ItemInstance CaoPenalty()
        {
            Random random = new Random();
            int rnd = random.Next( _items.Count );
            L1ItemInstance penaltyItem = (L1ItemInstance)_items[rnd];
            if ( penaltyItem.getItem().getItemId() == L1ItemId.ADENA // アデナ、トレード不可のアイテムは落とさない
                    || !penaltyItem.getItem().isTradable() )
            {
                return null;
            }
            //Object[] petlist = _owner.getPetList().values().toArray();
            foreach ( Object petObject in _owner.getPetList().Values )
            {
                if ( petObject is L1PetInstance )
                {
                    L1PetInstance pet = (L1PetInstance)petObject;
                    if ( penaltyItem.getId() == pet.getItemObjId() )
                    {
                        return null;
                    }
                }
            }
            setEquipped( penaltyItem, false );
            return penaltyItem;
        }
    }
}
